from django.utils import timezone
from django.db.models import Sum, Avg, Q
from decimal import Decimal
from datetime import datetime, timedelta, date
from typing import Dict, List, Optional, Any
import json
import logging

from portfolios.models import Portfolio, PortfolioHolding, Transaction
from portfolios.monitoring_models import (
    MonitoringRule, PortfolioMonitoring, Alert, QuarterlyReport,
    MarketIndicator, RebalanceRecommendation
)
from data_service.models import FundNavDetail
from products.models import Product

logger = logging.getLogger(__name__)

class MonitoringService:
    """投资组合监控服务"""
    
    def __init__(self):
        # 默认监控阈值
        self.default_thresholds = {
            'allocation_deviation': Decimal('0.05'),  # 配置偏离阈值5%
            'concentration_risk': Decimal('0.30'),    # 单一产品限制30%
            'stop_loss_warning': Decimal('0.90'),     # 止损预警90%
            'performance_deviation': Decimal('0.10'),  # 业绩偏离10%
            'bond_yield_change': Decimal('0.005'),    # 国债收益率变化0.5%
        }
    
    def monitor_portfolio_allocation(self, portfolio_id: int) -> Dict[str, Any]:
        """监控投资组合配置偏离"""
        try:
            portfolio = Portfolio.objects.get(id=portfolio_id)
            holdings = PortfolioHolding.objects.filter(portfolio=portfolio)
            
            if not holdings.exists():
                return {'success': False, 'error': '投资组合无持仓数据'}
            
            # 计算当前配置
            current_allocation = self._calculate_current_allocation(portfolio)
            
            # 获取目标配置
            target_allocation = {
                'stable': portfolio.stable_ratio,
                'balanced': portfolio.balanced_ratio,
                'growth': portfolio.growth_ratio,
                'aggressive': portfolio.aggressive_ratio
            }
            
            # 检查偏离度
            deviations = {}
            alerts_created = []
            
            for asset_type, target_ratio in target_allocation.items():
                current_ratio = current_allocation.get(asset_type, Decimal('0'))
                deviation = abs(current_ratio - target_ratio)
                
                if deviation > self.default_thresholds['allocation_deviation']:
                    deviations[asset_type] = {
                        'current': float(current_ratio),
                        'target': float(target_ratio),
                        'deviation': float(deviation),
                        'status': 'over_threshold'
                    }
                    
                    # 创建预警
                    alert = self._create_allocation_alert(
                        portfolio, asset_type, current_ratio, target_ratio, deviation
                    )
                    alerts_created.append(alert.id)
                else:
                    deviations[asset_type] = {
                        'current': float(current_ratio),
                        'target': float(target_ratio),
                        'deviation': float(deviation),
                        'status': 'normal'
                    }
            
            # 记录监控结果
            monitoring_record = self._create_monitoring_record(
                portfolio, 'allocation_deviation', deviations
            )
            
            return {
                'success': True,
                'portfolio_id': portfolio_id,
                'deviations': deviations,
                'alerts_created': alerts_created,
                'monitoring_record_id': monitoring_record.id
            }
            
        except Portfolio.DoesNotExist:
            return {'success': False, 'error': '投资组合不存在'}
        except Exception as e:
            logger.error(f"监控配置偏离失败: {e}")
            return {'success': False, 'error': str(e)}
    
    def monitor_concentration_risk(self, portfolio_id: int) -> Dict[str, Any]:
        """监控集中度风险"""
        try:
            portfolio = Portfolio.objects.get(id=portfolio_id)
            holdings = PortfolioHolding.objects.filter(portfolio=portfolio)
            
            if not holdings.exists():
                return {'success': False, 'error': '投资组合无持仓数据'}
            
            total_value = portfolio.current_value
            violations = []
            alerts_created = []
            
            for holding in holdings:
                # 计算单一产品占比
                product_ratio = holding.current_amount / total_value if total_value > 0 else Decimal('0')
                
                if product_ratio > self.default_thresholds['concentration_risk']:
                    violation = {
                        'product_id': holding.product.id,
                        'product_name': holding.product.name,
                        'current_ratio': float(product_ratio),
                        'limit': float(self.default_thresholds['concentration_risk']),
                        'excess': float(product_ratio - self.default_thresholds['concentration_risk'])
                    }
                    violations.append(violation)
                    
                    # 创建集中度风险预警
                    alert = self._create_concentration_alert(portfolio, holding, product_ratio)
                    alerts_created.append(alert.id)
            
            # 记录监控结果
            monitoring_record = self._create_monitoring_record(
                portfolio, 'concentration_risk', {'violations': violations}
            )
            
            return {
                'success': True,
                'portfolio_id': portfolio_id,
                'violations': violations,
                'alerts_created': alerts_created,
                'monitoring_record_id': monitoring_record.id
            }
            
        except Portfolio.DoesNotExist:
            return {'success': False, 'error': '投资组合不存在'}
        except Exception as e:
            logger.error(f"监控集中度风险失败: {e}")
            return {'success': False, 'error': str(e)}
    
    def monitor_performance_tracking(self, portfolio_id: int) -> Dict[str, Any]:
        """监控业绩跟踪"""
        try:
            portfolio = Portfolio.objects.get(id=portfolio_id)
            
            # 计算投资组合业绩
            performance_data = self._calculate_portfolio_performance(portfolio)
            
            # 获取基准业绩（可以是市场指数或目标收益率）
            benchmark_performance = self._get_benchmark_performance(portfolio)
            
            # 计算业绩偏离
            performance_deviation = abs(
                performance_data['return_rate'] - benchmark_performance['return_rate']
            )
            
            alerts_created = []
            
            # 检查是否需要预警
            if performance_deviation > self.default_thresholds['performance_deviation']:
                alert = self._create_performance_alert(
                    portfolio, performance_data, benchmark_performance, performance_deviation
                )
                alerts_created.append(alert.id)
            
            # 记录监控结果
            monitoring_record = self._create_monitoring_record(
                portfolio, 'performance_tracking', {
                    'portfolio_performance': performance_data,
                    'benchmark_performance': benchmark_performance,
                    'deviation': float(performance_deviation)
                }
            )
            
            return {
                'success': True,
                'portfolio_id': portfolio_id,
                'performance_data': performance_data,
                'benchmark_performance': benchmark_performance,
                'deviation': float(performance_deviation),
                'alerts_created': alerts_created,
                'monitoring_record_id': monitoring_record.id
            }
            
        except Portfolio.DoesNotExist:
            return {'success': False, 'error': '投资组合不存在'}
        except Exception as e:
            logger.error(f"监控业绩跟踪失败: {e}")
            return {'success': False, 'error': str(e)}
    
    def generate_quarterly_report(self, portfolio_id: int, quarter: Optional[str] = None) -> Dict[str, Any]:
        """生成季度报告"""
        try:
            portfolio = Portfolio.objects.get(id=portfolio_id)
            
            if not quarter:
                quarter = self._get_current_quarter()
            
            # 检查是否已存在报告
            existing_report = QuarterlyReport.objects.filter(
                portfolio=portfolio, quarter=quarter
            ).first()
            
            if existing_report:
                return {
                    'success': True,
                    'report_id': existing_report.id,
                    'message': '季度报告已存在'
                }
            
            # 获取季度日期范围
            start_date, end_date = self._get_quarter_dates(quarter)
            
            # 计算季度业绩数据
            performance_data = self._calculate_quarterly_performance(
                portfolio, start_date, end_date
            )
            
            # 分析配置情况
            allocation_analysis = self._analyze_allocation_changes(
                portfolio, start_date, end_date
            )
            
            # 分析持仓表现
            holdings_performance = self._analyze_holdings_performance(
                portfolio, start_date, end_date
            )
            
            # 生成调整建议
            recommendations = self._generate_adjustment_recommendations(
                portfolio, performance_data, allocation_analysis
            )
            
            # 创建季度报告
            report = QuarterlyReport.objects.create(
                portfolio=portfolio,
                quarter=quarter,
                title=f"{portfolio.name} {quarter} 季度报告",
                start_date=start_date,
                end_date=end_date,
                start_value=performance_data['start_value'],
                end_value=performance_data['end_value'],
                total_return=performance_data['total_return'],
                benchmark_return=performance_data.get('benchmark_return'),
                volatility=performance_data.get('volatility'),
                max_drawdown=performance_data.get('max_drawdown'),
                sharpe_ratio=performance_data.get('sharpe_ratio'),
                allocation_analysis=allocation_analysis,
                holdings_performance=holdings_performance,
                recommendations=recommendations,
                status='completed'
            )
            
            return {
                'success': True,
                'report_id': report.id,
                'quarter': quarter,
                'performance_data': performance_data,
                'recommendations': recommendations
            }
            
        except Portfolio.DoesNotExist:
            return {'success': False, 'error': '投资组合不存在'}
        except Exception as e:
            logger.error(f"生成季度报告失败: {e}")
            return {'success': False, 'error': str(e)}
    
    def check_all_portfolios(self, user_id: Optional[int] = None) -> Dict[str, Any]:
        """检查所有投资组合的监控条件"""
        try:
            # 获取需要监控的投资组合
            portfolios = Portfolio.objects.filter(status='active')
            if user_id:
                portfolios = portfolios.filter(user_id=user_id)
            
            monitoring_results = {
                'total_portfolios': portfolios.count(),
                'allocation_alerts': 0,
                'concentration_alerts': 0,
                'performance_alerts': 0,
                'total_alerts': 0,
                'portfolio_results': []
            }
            
            for portfolio in portfolios:
                portfolio_result = {
                    'portfolio_id': portfolio.id,
                    'portfolio_name': portfolio.name,
                    'alerts': []
                }
                
                # 检查配置偏离
                allocation_result = self.monitor_portfolio_allocation(portfolio.id)
                if allocation_result['success'] and allocation_result.get('alerts_created'):
                    portfolio_result['alerts'].extend(allocation_result['alerts_created'])
                    monitoring_results['allocation_alerts'] += len(allocation_result['alerts_created'])
                
                # 检查集中度风险
                concentration_result = self.monitor_concentration_risk(portfolio.id)
                if concentration_result['success'] and concentration_result.get('alerts_created'):
                    portfolio_result['alerts'].extend(concentration_result['alerts_created'])
                    monitoring_results['concentration_alerts'] += len(concentration_result['alerts_created'])
                
                # 检查业绩跟踪
                performance_result = self.monitor_performance_tracking(portfolio.id)
                if performance_result['success'] and performance_result.get('alerts_created'):
                    portfolio_result['alerts'].extend(performance_result['alerts_created'])
                    monitoring_results['performance_alerts'] += len(performance_result['alerts_created'])
                
                monitoring_results['portfolio_results'].append(portfolio_result)
            
            monitoring_results['total_alerts'] = (
                monitoring_results['allocation_alerts'] +
                monitoring_results['concentration_alerts'] +
                monitoring_results['performance_alerts']
            )
            
            return {
                'success': True,
                'monitoring_results': monitoring_results
            }
            
        except Exception as e:
            logger.error(f"检查所有投资组合失败: {e}")
            return {'success': False, 'error': str(e)}
    
    def get_portfolio_alerts(self, portfolio_id: int, status: str = 'active') -> List[Dict[str, Any]]:
        """获取投资组合预警"""
        try:
            alerts = Alert.objects.filter(
                portfolio_id=portfolio_id,
                status=status
            ).order_by('-created_at')
            
            alert_list = []
            for alert in alerts:
                alert_data = {
                    'id': alert.id,
                    'alert_type': alert.alert_type,
                    'alert_level': alert.alert_level,
                    'title': alert.title,
                    'message': alert.message,
                    'alert_data': alert.alert_data,
                    'status': alert.status,
                    'created_at': alert.created_at.isoformat(),
                    'acknowledged_at': alert.acknowledged_at.isoformat() if alert.acknowledged_at else None
                }
                alert_list.append(alert_data)
            
            return alert_list
            
        except Exception as e:
            logger.error(f"获取投资组合预警失败: {e}")
            return []
    
    # 私有辅助方法
    
    def _calculate_current_allocation(self, portfolio: Portfolio) -> Dict[str, Decimal]:
        """计算当前资产配置"""
        holdings = PortfolioHolding.objects.filter(portfolio=portfolio)
        total_value = portfolio.current_value
        
        allocation = {
            'stable': Decimal('0'),
            'balanced': Decimal('0'),
            'growth': Decimal('0'),
            'aggressive': Decimal('0')
        }
        
        if total_value <= 0:
            return allocation
        
        # 根据产品类型分类计算
        for holding in holdings:
            product_value = holding.current_amount
            ratio = product_value / total_value
            
            # 根据产品风险等级分类（简化处理）
            risk_level = getattr(holding.product, 'risk_level', 3)
            if risk_level <= 2:
                allocation['stable'] += ratio
            elif risk_level == 3:
                allocation['balanced'] += ratio
            elif risk_level == 4:
                allocation['growth'] += ratio
            else:
                allocation['aggressive'] += ratio
        
        return allocation
    
    def _create_allocation_alert(self, portfolio: Portfolio, asset_type: str, 
                               current_ratio: Decimal, target_ratio: Decimal, 
                               deviation: Decimal) -> Alert:
        """创建配置偏离预警"""
        alert_data = {
            'asset_type': asset_type,
            'current_ratio': float(current_ratio),
            'target_ratio': float(target_ratio),
            'deviation': float(deviation),
            'threshold': float(self.default_thresholds['allocation_deviation'])
        }
        
        alert = Alert.objects.create(
            portfolio=portfolio,
            alert_type='allocation_deviation',
            alert_level='warning' if deviation < Decimal('0.10') else 'critical',
            title=f"{asset_type}配置偏离预警",
            message=f"{asset_type}类资产当前配置比例{current_ratio:.2%}，目标比例{target_ratio:.2%}，偏离度{deviation:.2%}",
            alert_data=alert_data
        )
        
        return alert
    
    def _create_concentration_alert(self, portfolio: Portfolio, holding: PortfolioHolding, 
                                  product_ratio: Decimal) -> Alert:
        """创建集中度风险预警"""
        alert_data = {
            'product_id': holding.product.id,
            'product_name': holding.product.name,
            'current_ratio': float(product_ratio),
            'limit': float(self.default_thresholds['concentration_risk'])
        }
        
        alert = Alert.objects.create(
            portfolio=portfolio,
            alert_type='concentration_risk',
            alert_level='warning' if product_ratio < Decimal('0.40') else 'critical',
            title=f"单一产品集中度风险预警",
            message=f"产品{holding.product.name}占比{product_ratio:.2%}，超过限制{self.default_thresholds['concentration_risk']:.2%}",
            alert_data=alert_data
        )
        
        return alert
    
    def _create_performance_alert(self, portfolio: Portfolio, performance_data: Dict, 
                                benchmark_data: Dict, deviation: Decimal) -> Alert:
        """创建业绩偏离预警"""
        alert_data = {
            'portfolio_return': performance_data['return_rate'],
            'benchmark_return': benchmark_data['return_rate'],
            'deviation': float(deviation)
        }
        
        alert = Alert.objects.create(
            portfolio=portfolio,
            alert_type='performance_tracking',
            alert_level='warning',
            title=f"业绩偏离预警",
            message=f"投资组合收益率{performance_data['return_rate']:.2%}，基准收益率{benchmark_data['return_rate']:.2%}，偏离度{deviation:.2%}",
            alert_data=alert_data
        )
        
        return alert
    
    def _create_monitoring_record(self, portfolio: Portfolio, monitoring_type: str, 
                                data: Dict) -> PortfolioMonitoring:
        """创建监控记录"""
        # 这里需要先创建或获取监控规则
        rule, created = MonitoringRule.objects.get_or_create(
            monitoring_type=monitoring_type,
            defaults={
                'name': f"{monitoring_type}监控",
                'threshold_value': self.default_thresholds.get(monitoring_type, Decimal('0.05')),
                'comparison_operator': 'gt'
            }
        )
        
        record = PortfolioMonitoring.objects.create(
            portfolio=portfolio,
            monitoring_rule=rule,
            current_value=Decimal('0'),  # 根据具体情况设置
            threshold_value=rule.threshold_value,
            deviation=Decimal('0'),  # 根据具体情况设置
            is_triggered=bool(data.get('alerts_created')),
            trigger_reason=json.dumps(data, ensure_ascii=False)
        )
        
        return record
    
    def _calculate_portfolio_performance(self, portfolio: Portfolio) -> Dict[str, Any]:
        """计算投资组合业绩"""
        # 简化实现，实际应该根据历史数据计算
        return {
            'return_rate': float(portfolio.total_return) if hasattr(portfolio, 'total_return') else 0.0,
            'current_value': float(portfolio.current_value),
            'total_amount': float(portfolio.total_amount)
        }
    
    def _get_benchmark_performance(self, portfolio: Portfolio) -> Dict[str, Any]:
        """获取基准业绩"""
        # 简化实现，可以根据市场指数或目标收益率计算
        return {
            'return_rate': 0.05,  # 假设基准收益率5%
            'name': '市场基准'
        }
    
    def _get_current_quarter(self) -> str:
        """获取当前季度"""
        now = timezone.now()
        quarter = (now.month - 1) // 3 + 1
        return f"{now.year}Q{quarter}"
    
    def _get_quarter_dates(self, quarter: str) -> tuple:
        """获取季度日期范围"""
        year, q = quarter.split('Q')
        year = int(year)
        quarter_num = int(q)
        
        start_month = (quarter_num - 1) * 3 + 1
        if quarter_num == 4:
            end_month = 12
            end_day = 31
        else:
            end_month = quarter_num * 3
            end_day = 30 if end_month in [4, 6, 9, 11] else 31
        
        start_date = date(year, start_month, 1)
        end_date = date(year, end_month, end_day)
        
        return start_date, end_date
    
    def _calculate_quarterly_performance(self, portfolio: Portfolio, 
                                       start_date: date, end_date: date) -> Dict[str, Any]:
        """计算季度业绩"""
        # 简化实现
        return {
            'start_value': float(portfolio.total_amount),
            'end_value': float(portfolio.current_value),
            'total_return': float(portfolio.total_return) if hasattr(portfolio, 'total_return') else 0.0,
            'volatility': 0.15,  # 假设波动率
            'max_drawdown': -0.05,  # 假设最大回撤
            'sharpe_ratio': 1.2  # 假设夏普比率
        }
    
    def _analyze_allocation_changes(self, portfolio: Portfolio, 
                                  start_date: date, end_date: date) -> Dict[str, Any]:
        """分析配置变化"""
        current_allocation = self._calculate_current_allocation(portfolio)
        return {
            'current_allocation': {k: float(v) for k, v in current_allocation.items()},
            'target_allocation': {
                'stable': float(portfolio.stable_ratio),
                'balanced': float(portfolio.balanced_ratio),
                'growth': float(portfolio.growth_ratio),
                'aggressive': float(portfolio.aggressive_ratio)
            }
        }
    
    def _analyze_holdings_performance(self, portfolio: Portfolio, 
                                    start_date: date, end_date: date) -> List[Dict[str, Any]]:
        """分析持仓表现"""
        holdings = PortfolioHolding.objects.filter(portfolio=portfolio)
        performance_list = []
        
        for holding in holdings:
            performance = {
                'product_id': holding.product.id,
                'product_name': holding.product.name,
                'return_rate': float(holding.profit_loss_rate) if hasattr(holding, 'profit_loss_rate') else 0.0,
                'contribution': float(holding.current_amount) / float(portfolio.current_value) if portfolio.current_value > 0 else 0.0,
                'current_value': float(holding.current_amount)
            }
            performance_list.append(performance)
        
        return performance_list
    
    def _generate_adjustment_recommendations(self, portfolio: Portfolio, 
                                           performance_data: Dict, 
                                           allocation_analysis: Dict) -> List[Dict[str, Any]]:
        """生成调整建议"""
        recommendations = []
        
        current_allocation = allocation_analysis['current_allocation']
        target_allocation = allocation_analysis['target_allocation']
        
        for asset_type, target_ratio in target_allocation.items():
            current_ratio = current_allocation.get(asset_type, 0)
            deviation = abs(current_ratio - target_ratio)
            
            if deviation > 0.05:  # 偏离超过5%
                action = '减持' if current_ratio > target_ratio else '增持'
                recommendation = {
                    'type': 'allocation_adjustment',
                    'asset_type': asset_type,
                    'action': action,
                    'current_ratio': current_ratio,
                    'target_ratio': target_ratio,
                    'deviation': deviation,
                    'priority': 'high' if deviation > 0.10 else 'medium',
                    'description': f"建议{action}{asset_type}类资产，当前配置{current_ratio:.1%}，目标配置{target_ratio:.1%}"
                }
                recommendations.append(recommendation)
        
        return recommendations